# This file is part of the Lumenn project 
# at http://code.google.com/p/lumenn/
#
# Released under the GPLv3 at http://www.gnu.org/copyleft/gpl.html
# Designed for pygtk and the Maemo platform.
#
# Copyright 2008 by Jackson Yee (jackson@gotpossum.com)

import pygtk
pygtk.require('2.0')

import gtk
import gtk.gdk as gdk
import gobject
import pango
import time
import utils
import os
import os.path
import math
import random
import cairo
import pangocairo

from xml.parsers.expat import ParserCreate

import lumenn.cairofx as cairofx
import lumenn.geometry as geometry

# *********************************************************************
class DocCtrl(object):
	
	# -------------------------------------------------------------------
	def __init__(self, name, attributes, parent):
		self.name				=	name
		self.attributes	=	attributes
		self.styles			=	[]
		self.children		=	[]
		self.parent			=	parent
		self.rt					=	geometry.Rect()
		
		if 'style' in self.attributes:
			sty	=	self.attributes['style']
			pts	=	sty.split(';')
			for s in pts:
				rule	=	s.split(':')
				self.styles[ rule[0] ]	=	rule[1]

# *********************************************************************
class DocTree(object):
	
	# -------------------------------------------------------------------
	def __init__(self):
		self.Clear()
		self.parser	=	ParserCreate()
		
		self.parser.StartElementHandler		=	self.StartElementHandler
		self.parser.EndElementHandler			=	self.EndElementHandler
		self.parser.CharacterDataHandler	=	self.CharacterDataHandler
		
	# -------------------------------------------------------------------
	def Clear(self):
		self.tree				=	None
		self.css				=	Style()
		
		self.css.LoadDefaults()
		
	# -------------------------------------------------------------------
	def Load(self, text):
		self.topelement				=	None
		self.currentelement		=	None
		self.parser.Parse(text)
		
	# -------------------------------------------------------------------
	def StartElementHandler(self, name, attrs):
		if self.currentelement:
			self.currentelement.children.append( 
				DocCtrl(name, attrs, self.currentelement) 
			)
		else:
			self.currentelement	=	DocCtrl(name, attrs, None) 
			self.topelement			=	self.currentelement
		
	# -------------------------------------------------------------------
	def EndElementHandler(self, name):
		if self.currentelement:
			self.currentelement	=	self.currentelement.parent
		
	# -------------------------------------------------------------------
	def CharacterDataHandler(self, text):
		t	=	text.strip()
		
		if t:
			self.currentelement.children.append(t)

# *********************************************************************
class Style(object):
	"""Object meant to contain CSS-type properties for the ThumbPad
	control to draw its items.
	
	Attributes can be any of the following as defined in _defaults
	"""
	_defaults	=	{
		'color'							:	cairofx.black,
		'background-color'	:	cairofx.white,
		'background-image'	:	None,
		'text-align'				:	'left',
		'font'							:	None,
		'font-family'				:	'serif',
		'font-size'					:	18,
		'font-style'				:	'normal',
		'font-weight'				:	'normal',
		'font-stretch'				:	'normal',
		'border-color'			:	None,
		'border-style'			:	None,
		'border-width'			:	0,
		'margin-left'				:	0,
		'margin-right'			:	0,
		'margin-top'				:	0,
		'margin-bottom'			:	0,
		'padding-left'			:	0,
		'padding-right'			:	0,
		'padding-top'				:	0,
		'padding-bottom'		:	0,
		'left'							:	'auto',
		'top'								:	'auto',
		'width'							:	'auto',
		'height'						:	'auto',
		'display'						:	'block',
		'float'							:	'none',
	}
	
	# -------------------------------------------------------------------
	def __init__(self):
		self.items		=	{}
		self._changed	=	[ [] ]
		self.library	=	{}
		
	# -------------------------------------------------------------------
	def __getitem__(self, k):
		if k in self.items:
			return self.items[k]
		
		raise AttributeError('%s does not have attribute %s'
			% (self.__class__, k)
		)
		
	# -------------------------------------------------------------------
	def __setitem__(self, k, v):
		self._changed[-1].append( (k, self.items[k]) )
		self.items[k]	=	v
		
	# -------------------------------------------------------------------
	def LoadDefaults(self):
		self.items	=	self._defaults.copy()
		self._changed	=	[ [] ]		
		
	# -------------------------------------------------------------------
	def Save(self):
		self._changed.append( [] )
		
	# -------------------------------------------------------------------
	def Restore(self):
		changed	=	self._changed.pop()
		
		while True:
			if not changed:
				break
			
			i	=	changed.pop()
			self.items[ i[0] ]	=	i[1]
			
	# -------------------------------------------------------------------
	def AddStyles(self, name, styles):
		self.library[name]	=	styles
		
	# -------------------------------------------------------------------
	def LoadStyle(self, name):
		self.Save()
		
		if name in self.library:
			for k, v in self.library[name].itervalues():
				self.__setitem__(k, v)				
	
# *********************************************************************
class ThumbPad(gtk.DrawingArea):
	"""PyGTK stack-based control for touchscreens."""
	
	# -------------------------------------------------------------------
	def __init__(self):
		gtk.DrawingArea.__init__(self)
		
		self.elements	=	[]
		
		self.Clear()
		
		self.connect("expose_event", self.OnExpose)
		self.connect("button_press_event", self.OnPress)
		self.connect("button_release_event", self.OnRelease)
		self.connect("motion_notify_event", self.OnMotion)
		self.connect("configure_event", self.OnConfigure)
		
		self.set_events(gtk.gdk.EXPOSURE_MASK
			| gtk.gdk.BUTTON_PRESS_MASK
			| gtk.gdk.BUTTON_RELEASE_MASK
			| gtk.gdk.POINTER_MOTION_MASK
			| gtk.gdk.POINTER_MOTION_HINT_MASK)
	
	# -------------------------------------------------------------------
	def OnExpose(self, widget, event):
		x1, y1, w, h = self.get_allocation()
		
		rt = geometry.Rect(0, 0, w, h)
		
		if not rt.Valid():
			return
			
		x	=	0
		y	=	0
		
		fx = cairofx.CairoFX( self.window.cairo_create() )
		
		return False
		
	# -------------------------------------------------------------------
	def OnConfigure(self, widget, event):
		self.DoLayout()
		
	# -------------------------------------------------------------------
	def DoLayout(self):
		pass
		
	# -------------------------------------------------------------------
	def OnMotion(self, widget, event):
		if not self.indrag:
			return False

		if event.is_hint:
			x, y, state = event.window.get_pointer()
		else:
			x, y, state = event.x, event.y, event.state

		self.momentumy = self.lasty - y
		self.lasty = y
		self.viewy -= self.momentumy

		if self.viewy < 0:
			self.viewy = 0

		if self.viewy > self.maxy - self.dh:
			self.viewy = self.maxy - self.dh

		self.queue_draw()
		return False

	# -------------------------------------------------------------------
	def OnPress(self, widget, event):
		if event.type == gtk.gdk.BUTTON_PRESS:
			if event.button == 1:
				self.indrag = True
				self.lastx, self.lasty = event.x, event.y

				self.clickx	=	event.x
				self.clicky	=	event.y

		return True

	# -------------------------------------------------------------------
	def OnRelease(self, widget, event):
		if event.button == 1:
			self.indrag = False
			x, y, state = event.x, event.y, event.state
			self.lasty = 0

			dx = self.clickx - x
			dy = self.clicky - y

			if dx >= -16 and dx <= 16 and dy >= -16 and dy <= 16:
				self.OnLeftClick(widget, event)

		return True
		
	# -------------------------------------------------------------------
	def OnLeftClick(self, widget, event):
		x, y = event.x, event.y		
		
		for k, v in self.static.iteritems():
			if v.onclick:
				if v.rt.Contains(x, y):
					v.OnClick()
					return
		
		x = x + self.x * 4
		y = y + self.y * 4
		
		for k, v in self.scrolled.iteritems():
			if v.onclick:
				if v.rt.Contains(x, y):
					v.OnClick()
					return
		
	# -------------------------------------------------------------------
	def OnConfigure(self, widget, event):
		self.DoLayout()
		self.queue_draw()
						
	# -------------------------------------------------------------------
	def Clear(self):
		self.x, self.y = 0, 0
		self.lastx, self.lasty = 0, 0
		
		self.indrag = False
		self.selected 		= 0
		self.viewy 				= 0
		self.momentumy		=	0
		self.maxy					=	0
		self.dh						=	0
		self.clickx, self.clicky	=	0, 0
		self.lastx, self.lasty = 0, 0
		self.border	=	4
		
		self.static		=	{}
		self.scrolled	=	{}
		self.scrolledrect	=	geometry.Rect()
		
		self.items		=	{}
		self.tree			=	DocTree()
		
	# -------------------------------------------------------------------
	def Load(self, page):
		self.tree.Load(page)
		

		
